// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef GPU_COMMAND_BUFFER_SERVICE_VERTEX_ATTRIB_MANAGER_H_
#define GPU_COMMAND_BUFFER_SERVICE_VERTEX_ATTRIB_MANAGER_H_

#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "build/build_config.h"
#include "gpu/command_buffer/service/buffer_manager.h"
#include "gpu/command_buffer/service/gl_utils.h"
#include "gpu/gpu_export.h"
#include <list>
#include <vector>

namespace gpu {
namespace gles2 {

    class FeatureInfo;
    class GLES2Decoder;
    class Program;
    class VertexArrayManager;

    // Info about a Vertex Attribute. This is used to track what the user currently
    // has bound on each Vertex Attribute so that checking can be done at
    // glDrawXXX time.
    class GPU_EXPORT VertexAttrib {
    public:
        typedef std::list<VertexAttrib*> VertexAttribList;

        VertexAttrib();
        ~VertexAttrib();

        // Returns true if this VertexAttrib can access index.
        bool CanAccess(GLuint index) const;

        Buffer* buffer() const { return buffer_.get(); }

        GLsizei offset() const
        {
            return offset_;
        }

        GLuint index() const
        {
            return index_;
        }

        GLint size() const
        {
            return size_;
        }

        GLenum type() const
        {
            return type_;
        }

        GLboolean normalized() const
        {
            return normalized_;
        }

        GLsizei gl_stride() const
        {
            return gl_stride_;
        }

        GLuint divisor() const
        {
            return divisor_;
        }

        GLboolean integer() const
        {
            return integer_;
        }

        bool enabled() const
        {
            return enabled_;
        }

        // Find the maximum vertex accessed, accounting for instancing.
        GLuint MaxVertexAccessed(GLsizei primcount,
            GLuint max_vertex_accessed) const
        {
            return divisor_ ? ((primcount - 1) / divisor_) : max_vertex_accessed;
        }

        bool is_client_side_array() const
        {
            return is_client_side_array_;
        }

        void set_is_client_side_array(bool value)
        {
            is_client_side_array_ = value;
        }

    private:
        friend class VertexAttribManager;

        void set_enabled(bool enabled)
        {
            enabled_ = enabled;
        }

        void set_index(GLuint index)
        {
            index_ = index;
        }

        void SetList(VertexAttribList* new_list)
        {
            DCHECK(new_list);

            if (list_) {
                list_->erase(it_);
            }

            it_ = new_list->insert(new_list->end(), this);
            list_ = new_list;
        }

        void SetInfo(
            Buffer* buffer,
            GLint size,
            GLenum type,
            GLboolean normalized,
            GLsizei gl_stride,
            GLsizei real_stride,
            GLsizei offset,
            GLboolean integer);

        void SetDivisor(GLsizei divisor)
        {
            divisor_ = divisor;
        }

        void Unbind(Buffer* buffer);

        // The index of this attrib.
        GLuint index_;

        // Whether or not this attribute is enabled.
        bool enabled_;

        // number of components (1, 2, 3, 4)
        GLint size_;

        // GL_BYTE, GL_FLOAT, etc. See glVertexAttribPointer.
        GLenum type_;

        // The offset into the buffer.
        GLsizei offset_;

        GLboolean normalized_;

        // The stride passed to glVertexAttribPointer.
        GLsizei gl_stride_;

        // The stride that will be used to access the buffer. This is the actual
        // stide, NOT the GL bogus stride. In other words there is never a stride
        // of 0.
        GLsizei real_stride_;

        GLsizei divisor_;

        GLboolean integer_;

        // Will be true if this was assigned to a client side array.
        bool is_client_side_array_;

        // The buffer bound to this attribute.
        scoped_refptr<Buffer> buffer_;

        // List this info is on.
        VertexAttribList* list_;

        // Iterator for list this info is on. Enabled/Disabled
        VertexAttribList::iterator it_;
    };

    // Manages vertex attributes.
    // This class also acts as the service-side representation of a
    // vertex array object and it's contained state.
    class GPU_EXPORT VertexAttribManager : public base::RefCounted<VertexAttribManager> {
    public:
        typedef std::list<VertexAttrib*> VertexAttribList;

        VertexAttribManager();

        void Initialize(uint32 num_vertex_attribs, bool init_attribs);

        bool Enable(GLuint index, bool enable);

        bool HaveFixedAttribs() const
        {
            return num_fixed_attribs_ != 0;
        }

        const VertexAttribList& GetEnabledVertexAttribs() const
        {
            return enabled_vertex_attribs_;
        }

        VertexAttrib* GetVertexAttrib(GLuint index)
        {
            if (index < vertex_attribs_.size()) {
                return &vertex_attribs_[index];
            }
            return NULL;
        }

        void SetAttribInfo(
            GLuint index,
            Buffer* buffer,
            GLint size,
            GLenum type,
            GLboolean normalized,
            GLsizei gl_stride,
            GLsizei real_stride,
            GLsizei offset,
            GLboolean integer)
        {
            VertexAttrib* attrib = GetVertexAttrib(index);
            if (attrib) {
                if (attrib->type() == GL_FIXED) {
                    --num_fixed_attribs_;
                }
                if (type == GL_FIXED) {
                    ++num_fixed_attribs_;
                }
                attrib->SetInfo(buffer, size, type, normalized, gl_stride, real_stride,
                    offset, integer);
            }
        }

        void SetDivisor(GLuint index, GLuint divisor)
        {
            VertexAttrib* attrib = GetVertexAttrib(index);
            if (attrib) {
                attrib->SetDivisor(divisor);
            }
        }

        void SetElementArrayBuffer(Buffer* buffer);

        Buffer* element_array_buffer() const { return element_array_buffer_.get(); }

        GLuint service_id() const
        {
            return service_id_;
        }

        void Unbind(Buffer* buffer);

        bool IsDeleted() const
        {
            return deleted_;
        }

        bool IsValid() const
        {
            return !IsDeleted();
        }

        size_t num_attribs() const
        {
            return vertex_attribs_.size();
        }

        bool ValidateBindings(
            const char* function_name,
            GLES2Decoder* decoder,
            FeatureInfo* feature_info,
            Program* current_program,
            GLuint max_vertex_accessed,
            bool instanced,
            GLsizei primcount);

    private:
        friend class VertexArrayManager;
        friend class VertexArrayManagerTest;
        friend class base::RefCounted<VertexAttribManager>;

        // Used when creating from a VertexArrayManager
        VertexAttribManager(VertexArrayManager* manager, GLuint service_id,
            uint32 num_vertex_attribs);

        ~VertexAttribManager();

        void MarkAsDeleted()
        {
            deleted_ = true;
        }

        // number of attribs using type GL_FIXED.
        int num_fixed_attribs_;

        // Info for each vertex attribute saved so we can check at glDrawXXX time
        // if it is safe to draw.
        std::vector<VertexAttrib> vertex_attribs_;

        // The currently bound element array buffer. If this is 0 it is illegal
        // to call glDrawElements.
        scoped_refptr<Buffer> element_array_buffer_;

        // Lists for which vertex attribs are enabled, disabled.
        VertexAttribList enabled_vertex_attribs_;
        VertexAttribList disabled_vertex_attribs_;

        // The VertexArrayManager that owns this VertexAttribManager
        VertexArrayManager* manager_;

        // True if deleted.
        bool deleted_;

        // Service side vertex array object id.
        GLuint service_id_;
    };

} // namespace gles2
} // namespace gpu

#endif // GPU_COMMAND_BUFFER_SERVICE_VERTEX_ATTRIB_MANAGER_H_
